package org.tlipoca.smarticle.service.impl;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.genntii.smcommon.constant.ExceptionConstant;
import org.genntii.smcommon.exception.EsException;
import org.genntii.smcommon.result.PageResult;
import org.genntii.smdomain.doc.ArticleDoc;
import org.genntii.smdomain.doc.VO.UserDocVO;
import org.genntii.smdomain.entity.Article;
import org.genntii.smdomain.entity.ArticleContent;
import org.genntii.smdomain.entity.ArticleTagRelational;
import org.genntii.smdomain.vo.ArticleDocHandleResponseVO;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.tlipoca.smarticle.client.ArticleTagClient;
import org.tlipoca.smarticle.client.ArticleTagRelationalClient;
import org.tlipoca.smarticle.client.UserClient;
import org.tlipoca.smarticle.service.EsArticleService;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class EsArticleServiceImpl implements EsArticleService {

    @Resource
    private RestHighLevelClient restHighLevelClient;

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private UserClient userClient;

    @Resource
    private ArticleTagRelationalClient articleTagRelationalClient;

    @Resource
    private MongoTemplate mongoTemplate;

    @Resource
    private ArticleTagClient articleTagClient;

    @Override
    public ArticleDocHandleResponseVO queryAllArticle() throws IOException {
        SearchRequest request = new SearchRequest("articledoc");
        request.source().query(QueryBuilders.matchAllQuery());
        SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        return handleResponse(response);
    }


    private static ArticleDocHandleResponseVO handleResponse(SearchResponse response) throws JsonProcessingException {
        SearchHits searchHits = response.getHits();
        // 4.1 获取总条数
        long total = 0L;
        if (searchHits.getTotalHits() != null) {
            total = searchHits.getTotalHits().value;
        }
        // 4.2 获取命中的数据
        SearchHit[] hits = searchHits.getHits();
        List<ArticleDoc> userDocList=new ArrayList<>();
        for (SearchHit hit : hits) {
            // 4.2.1 获取source结果(结果是一个json对象)
            String json = hit.getSourceAsString();
            // 4.2.2 转为实体对象
            ArticleDoc articleDoc = new ObjectMapper().readValue(json,ArticleDoc.class);
            userDocList.add(articleDoc);
        }
        System.out.println("userDocList = " + userDocList);
        System.out.println("total = " + total);
        return ArticleDocHandleResponseVO.builder()
                .userDocList(userDocList)
                .total(total)
                .build();
    }

    @Override
    public void insertArticle(Article article)  {
        UserDocVO userVO = userClient.getUserDocVOById(article.getUserId()).getData();
        ArticleContent articleContent = mongoTemplate.findOne(new Query(Criteria.where("_id").is(article.getId())),ArticleContent.class);
        ArrayList<ArticleTagRelational> a = articleTagRelationalClient.getArticleTagRelationalByArticleId(article.getId()).getData();
        ArrayList<Long> tagIds = new ArrayList<>();
        for (ArticleTagRelational articleTagRelational : a){
            tagIds.add(articleTagRelational.getTagId());
        }
        ArticleDoc articleDoc = ArticleDoc.builder()
                .id(article.getId())
                .userId(article.getUserId())
                .authorName(userVO.getNickname())
                .channelId(article.getChannelId())
                .title(article.getTitle())
                .articleTags(articleTagClient.getArticleTagDocVOLIstByTagId(tagIds).getData())
                .updateTime(String.valueOf(article.getUpdateTime()))
                .view(article.getView())
                .userArticleCount(article.getUserArticleCount())
                .userFollowerCount(article.getUserFollowerCount())
                .avatar(userVO.getAvatar())
                .build();
        if (Objects.requireNonNull(articleContent).getContent() != null){
            articleDoc.setContent(articleContent.getContent());
        }
        if (Objects.requireNonNull(articleContent).getCover() != null){
            articleDoc.setCover(articleContent.getCover());
        }

        try {
            IndexRequest request = new IndexRequest();
            request.index("articledoc").id(String.valueOf(articleDoc.getId()));

            String articleJson = objectMapper.writeValueAsString(articleDoc);

            request.source(articleJson, XContentType.JSON);
            IndexResponse response = restHighLevelClient.index(request,RequestOptions.DEFAULT);
            log.info(String.valueOf(response.getResult()));

        }catch (IOException e){
            throw new EsException(ExceptionConstant.ELASTIC_SEARCH_HANDLE_EXCEPTION);
        }



    }

    @Override
    public void deleteArticle(Article article) {

        try {
            DeleteRequest request = new DeleteRequest();
            request.index("articledoc").id(String.valueOf(article.getId()));

            DeleteResponse response = restHighLevelClient.delete(request,RequestOptions.DEFAULT);
            log.info(String.valueOf(response.getResult()));


        } catch (IOException e){
            throw new EsException(ExceptionConstant.ELASTIC_SEARCH_HANDLE_EXCEPTION);
        }
    }

    @Override
    public PageResult<ArticleDoc> search(String keyword, Integer pageNum, Integer pageSize) {
        SearchRequest request = new SearchRequest();
        request.indices("articledoc");

        SearchSourceBuilder builder = new SearchSourceBuilder();
        // 分页
        builder.from((pageNum-1)*pageSize);
        builder.size(pageSize);

        //模糊
        MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyword, "title", "content");
        builder.query(multiMatchQueryBuilder);

        // 高光
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        HighlightBuilder.Field highLightTitle = new HighlightBuilder.Field("title");
        HighlightBuilder.Field highLightContent = new HighlightBuilder.Field("content");
        highlightBuilder.field(highLightTitle);
        highlightBuilder.field(highLightContent);
        highlightBuilder.preTags("<font color = '#dd4b39'>");
        highlightBuilder.postTags("</font>");

        builder.highlighter(highlightBuilder);

        request.source(builder);
        SearchResponse response;
        try {
            response = restHighLevelClient.search(request,RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new EsException(ExceptionConstant.ELASTIC_SEARCH_HANDLE_EXCEPTION);
        }
        ArrayList<ArticleDoc> articleDocs = new ArrayList<>();
        for (SearchHit hit : response.getHits().getHits()) {
            // 获取高亮字段
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField titleHighlight = highlightFields.get("title");
            HighlightField contentHighlight = highlightFields.get("content");
            String highlightedTitle = null;
            String highlightedContent = null;

            if (titleHighlight != null) {
                Text[] fragments = titleHighlight.fragments();
                highlightedTitle = Arrays.stream(fragments)
                        .map(Text::string)
                        .collect(Collectors.joining());
            }

            if (contentHighlight != null) {
                Text[] fragments = contentHighlight.fragments();
                highlightedContent = Arrays.stream(fragments)
                        .map(Text::string)
                        .collect(Collectors.joining());
            }

            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            ArticleDoc doc = JSON.parseObject(JSON.toJSONString(sourceAsMap), ArticleDoc.class);
            if (highlightedTitle != null) {
                doc.setTitle(highlightedTitle);
            }
            if (highlightedContent != null) {
                doc.setContent(highlightedContent);
            }
            articleDocs.add(doc);
        }

        return PageResult.build(articleDocs, response.getHits().getTotalHits().value);
    }
}
